package org.geektimes.configuration.microprofile.config;


import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.ConfigValue;
import org.eclipse.microprofile.config.spi.ConfigSource;
import org.eclipse.microprofile.config.spi.ConfigSourceProvider;
import org.eclipse.microprofile.config.spi.Converter;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created with IntelliJ IDEA.
 * Description:
 *
 * @Author: menglinggang
 * @Date: 2021-03-15
 * @Time: 2:34 下午
 */
public class DefaultConfig implements Config {


    private List<ConfigSource> configSources = new LinkedList<>();

    private Map<Class<?>, Converter> converterMap = new ConcurrentHashMap<>();

    private Map<String, ConfigValue> cacheConfigValueMap = new ConcurrentHashMap<>();

    public DefaultConfig() {
        ClassLoader classLoader = getClass().getClassLoader();
        // load ConfigSourceProvider
        ServiceLoader<ConfigSourceProvider> load = ServiceLoader.load(ConfigSourceProvider.class, classLoader);
        load.forEach(item ->{
            item.getConfigSources(classLoader).forEach(configSources :: add);
        });
        configSources.sort((o1, o2) ->Integer.compare(o2.getOrdinal(), o1.getOrdinal()));
        // load Converter
        ServiceLoader<Converter> converters = ServiceLoader.load(Converter.class, classLoader);
        converters.forEach(converter -> {
            converterMap.put(converter.getClass(), converter);
        });
    }

    @Override
    public <T> T getValue(String s, Class<T> aClass) {
        if (s == null || "".equals(s.trim())) {
            return null;
        }
        Optional<ConfigSource> first = configSources.stream().filter(configSource -> configSource.getValue(s) != null).limit(1).findFirst();
        if (first.isPresent()) {
            String value = first.get().getValue(s);
            if (value != null) {
                if (String.class.equals(aClass)) {
                    return (T) value;
                }
                Optional<Converter<T>> converter = getConverter(aClass);
                if (converter.isPresent()) {
                    return converter.get().convert(value);
                } else {
                    throw new RuntimeException("has no converter to convert String to "+ aClass.getClass().getSimpleName());
                }
            }
        }
        return null;
    }

    @Override
    public ConfigValue getConfigValue(String s) {
        return null;
    }

    @Override
    public <T> Optional<T> getOptionalValue(String s, Class<T> aClass) {
        return Optional.ofNullable(getValue(s, aClass));
    }

    @Override
    public Iterable<String> getPropertyNames() {
        Set<String> names = new HashSet<>();
        configSources.forEach(configSource -> {
            names.addAll(configSource.getPropertyNames());
        });
        return Collections.unmodifiableSet(names);
    }

    @Override
    public Iterable<ConfigSource> getConfigSources() {
        return Collections.unmodifiableList(configSources);
    }

    @Override
    public <T> Optional<Converter<T>> getConverter(Class<T> aClass) {
        if (converterMap.isEmpty()) {
            return Optional.empty();
        }
        Converter converter = converterMap.get(aClass);
        if (converter == null) {
            return Optional.empty();
        }
        return Optional.of(converter);
    }

    @Override
    public <T> T unwrap(Class<T> aClass) {
        return null;
    }
}
